/*!
 * \file ast.h
 *
 * \brief Declarations for an abstract syntax tree for
 *        dealing with Inspector/Executor code generation.
 *
 * \date Started: 5/17/2010
 * \date Last Modified: 6/10/2010
 * \authors Ian Craig
 *
 * Copyright (c) 2010, Colorado State University <br>
 * All rights reserved. <br>
 * See ../../COPYING for details. <br>
 */

#ifndef AST_H_
#define AST_H_

#include <iegenlib/iegenlib.h>

#include <list>
using std::list;
#include <string>
using std::string;
#include<fstream>
using std::ofstream;
#include<stack>
using std::stack;

namespace iegenlib{

class VisitorAdapter;
class NormExp;

/*!
 * \class Node
 *
 * \brief Base AST node class
 *
 * This class is the base node class for all other classes that need a node
 * inside the AST. The class itself holds an apply method to be overloaded by
 * the children classes so that any visitors that extend VisitorAdapter can
 * easily access the node itself.
 */
class Node{
public:

  /*!
   * The apply method to be overloaded by the children classes
   * @param visitor The visitor that will be actually visiting the node
   */
  void apply(VisitorAdapter* visitor);

  /*!
   * @return the type of node as a string
   */
  virtual const string getNodeType() = 0;
};

/*!
 * \class Expression
 *
 * \brief Base Expression class for the AST
 *
 * This class is the base expression class for all other classes that are of
 * type expression (VarExp, FuncExp, NormExp). The class itself holds a toString
 * method to be overloaded by the children classes so that any other classes
 * needing a more specific expression's toString() method will be able to access
 * it
 *
 */
class Expression: public Node{
public:

  virtual ~Expression(){};

  /*!
   * The apply method to be overloaded by children expression classes
   * @param visitor The visitor that will be visiting the nodes
   */
  virtual void apply(VisitorAdapter* visitor) = 0;

  /*!
   * toString method that returns the expression in terms that the children
   * class should define
   * @return string form of child expression class
   */
  virtual const string toString() = 0;
};

/*!
 * \class VarExp
 *
 * \brief An expression holding a variable or a constant for the AST
 *
 * This class is the node for a variable expression, i.e. kx where k is some
 * coefficient and x is some variable name.
 */
class VarExp: public Expression{
public:

  /*!
   * VarExp constructor
   * @param co coefficient of the variable
   * @param inId the variable name
   */
  inline VarExp(int co, string inId): coeff(co), id(inId){}

  /*!
   * VarExp const constructor that represents a constant variable expression
   * @param co constant to be represented
   */
  inline VarExp(int co): coeff(co){}

  /*!
   * toString method that returns the variable expression in terms of
   * VarExp[CI], where c is the coefficient and I is the id.
   * @return string form of VarExp
   */
  const string toString();

  /*!
   * Calls visitor.VarExpCase(this)
   */
  void apply(VisitorAdapter* visitor);

  ///@return the coefficient of the variable
  inline const int getCoeff(){ return coeff; }

  ///set the coefficent
  inline void setCoeff(int co){coeff = co;}

  ///@return the id of the variable
  inline const string getId(){ return id; }

  inline const string getNodeType(){return "VarExp  "+id;}

private:

  ///Variable coefficient
  int coeff;

  ///Variable ID
  string id;
};

/*!
 * \class FuncExp
 *
 * \brief An expression representing a function inside of the AST
 *
 * This class is the node for a function expression, i.e f(x1, x2, x3,....,xn)
 * where f is the function name and x1,x2,x3,....,xn are the function parameters
 */
class FuncExp: public Expression{
public:

  /*!
   * FuncExp Constructor
   * @param co coefficient of the function
   * @param inName the name of the function
   * @param inArgs the list of parameters that are being sent into the function
   */
  inline FuncExp(int co, string inName, list<NormExp*>* inArgs): coeff(co), name(inName), args(*inArgs){}

  /*!
   * FuncExp Copy Constructor
   * Deep copy constructor, will create a new object for each object in FuncExp
   * @param fexp the FuncExp to copy
   */
  FuncExp(const FuncExp& fexp);

  /*!
   * FuncExp Destructor
   * Deep Destructor, will call the destructor on all objects in FuncExp even if
   * they are pointers
   */
  ~FuncExp();

  /*!
   * FuncExp operator=
   * Calls the copy constructor and returns a reference to a new FuncExp created
   * by fexp
   * @param fexp the FuncExp to be copied
   * @return a reference to the a new FuncExp that is copied from fexp
   */
  FuncExp& operator=(const FuncExp& fexp);

  /*!
   * toString method that returns the function expression in terms of
   * FuncExp[Cfname(x1,x2,x3,...xn)] where C is the coefficient, fname is the
   * fucntion name, and x1,x2,x3,....,xn are the parameter names
   * @return string form of FuncExp
   */
  const string toString();

  /*!
   * calls visitor.FuncExpCase(this)
   */
  void apply(VisitorAdapter* visitor);

  ///@return the beginning of the args list
  inline const list<NormExp*>::iterator begin(){return args.begin();}

  ///@return the end of the args list
  inline const list<NormExp*>::iterator end(){return args.end();}

  ///@return the coefficient of the function
  inline const int getCoeff(){return coeff;}

  ///@return the name of the function
  inline const string getName(){return name;}

  ///@return the coefficent of the FuncExp
  inline void setCoeff(int co){coeff = co;}

  ///@return the list of arguments of the function
  inline const list<NormExp*> getArgs(){return args;}

  inline const string getNodeType(){return "FuncExp";}

private:

  ///Function coefficient
  int coeff;

  ///Function name
  string name;

  ///Function paramters
  list<NormExp*> args;
};

/*!
 * \class NormExp
 *
 * \brief An expression representing a normal function in the AST
 *
 * This class is the node for a normal expression, i.e. a + b, a - c
 */
class NormExp: public Expression{
public:

  /*!
   * NormExp Const constructor which represents a constant expression
   * @param inConst The constatn value that will be represented
   */
  inline NormExp(int inConst): con(inConst){}

  /*!
   * NormExp constructor
   * @param inTerms Variables that are involved with the expression
   * @param inConst constants that are involved with the expression
   */
  inline NormExp(list<Expression*>* inTerms, int inConst): terms(*inTerms), con(inConst){}

  /*!
   * NormExp copy constructor
   * Deep copy constructor
   * @param nexp a normal expression that is copied
   */
  NormExp(const NormExp& nexp);

  /*!
   * NormExp Destructor
   * Deep destructor
   */
  ~NormExp();

  ///@return reference to new NormExp
  NormExp& operator=(const NormExp& rhs);

  /*!
   * Adds two Norm expressions together such that there is a new list of
   * expressions and with all of the expressions and the constants added to
   * a new constant.
   *
   * USAGE: NormExp = NormExp + NormExp
   *
   * RESTRICTIONS: none
   * @param lhs the left NormExp
   * @param rhs the right NormExp
   * @return new NormExp with appended expressions and constants added
   */
  friend NormExp operator+(const NormExp& lhs, const NormExp& rhs);

  /*!
   * Unary - operator, multiplies all the coefficents of the NormExp and the
   * constants by -1.
   *
   * USAGE: NormExp = -NormExp
   *
   * RESTRICTIONS: none
   * @param rhs the NormExpression to apply the unary operator to
   * @return new NormExp with the modified coefficents and constants
   */
  friend NormExp operator-(const NormExp& rhs);

  /*!
   * Subracts two Norm expressions together such that there is a new list of
   * expressions and with all of the expressions and the constants subtracted to
   * a new constant. Essentially this is NormExp + (-NormExp)
   *
   * USAGE: NormExp = NormExp - NormExp
   *
   * RESTRICTIONS: none
   * @param lhs the left NormExp
   * @param rhs the right NormExp
   * @return new NormExp with appended expressions and constants added
   */
  friend NormExp operator-(const NormExp& lhs, const NormExp& rhs);

  /*!
   * Multiplies two Norm expressions where one of the two expressions must be
   * a NormExp with NO TERMS, only a constant. This is due to the fact that we
   * only want linear equations, not exponential. They are multiplied such that
   * the terms and constant of the NormExp with terms is multiplied with the
   * termless NormExp's constant, i.e.
   *
   * NormExp(2x,3y,5) * NormExp(,2) = NormExp(4x, 6y, 10)
   *
   * USAGE: NormExp = NormExp * NormExp
   *
   * RESTRICTIONS: One of the two NormExp's MUST have NO TERMS (can have a
   *               constant)
   * @return new NormExp with the termless NormExp constant multiplied across
   *         the other NormExp's terms and constant
   */
  friend NormExp operator*(const NormExp& lhs, const NormExp& rhs);

  /*!
   * Multiplies a constant across a NormExp's terms and constant
   *
   * USAGE: NormExp = int * NormExp
   *
   * RESTRICTIONS: none
   * @param lhs constant to multiply over the normexp
   * @param rhs the NormExp to multiply the constant over
   * @return new NormExp with the constant multiplied across the norm
   */
  friend NormExp operator*(const int lhs, const NormExp& rhs);

  /*!
   * Multiplies a constant across a NormExp's terms and constant
   *
   * USAGE: NormExp = NormExp * int
   *
   * RESTRICTIONS: none
   * @param lhs the NormExp to multiply the constant over
   * @param rhs constant to multiply over the normexp
   * @return new NormExp with the constant multiplied across the norm
   */
  friend NormExp operator*(const NormExp& lhs, const int rhs);

  /*!
   * toString method that returns the normal expression in terms of
   * NormExp[(terms)const]
   * @return string form of NormExp
   */
  const string toString();

  /*!
   * returns a boolean describing if there are any terms
   * @return true if there are any terms
   */
  inline const bool hasTerms() const {return terms.size() != 0;}

  /*!
   * returns a boolean describing if there are no terms
   * @return true if there are no terms
   */
  inline const bool empty() const {return !hasTerms() && (con == 0);}

  /*!
   * returns the start of the list of terms
   * @return the iterator position at the start of the terms list
   */
  inline const list<Expression*>::iterator begin(){return terms.begin();}

  /*!
   * returns the end of the list of terms
   * @return the iterator position at the end of the terms list
   */
  inline const list<Expression*>::iterator end(){return terms.end();}

  /*!
   * Calls visitor.NormExpCase(this)
   */
  void apply(VisitorAdapter* visitor);

  ///@return the list of Expression pointers
  inline const list<Expression*> getTerms(){return terms;}

  ///@return the constant value
  inline const int getConst(){return con;}

  /*!
   * Sets the constant value
   * @param co the value to set the constant to
   */
  inline void setConst(int co){con = co;}

  inline const string getNodeType(){return "NormExp";}

private:

  ///Variable terms
  list<Expression*> terms;

  ///constant terms
  int con;
};

/*!
 * \class VarTuple
 *
 * \brief A list of variables and constants grouped together for the AST
 *
 * This class is the node for representing a group of variables together and is
 * used in several other classes, more specifically the PresSet and PresRelation
 * classes
 */
class VarTuple: public Node{
public:
  /*!
   * VarTuple Constructor
   * @param inVars list of variables in the VarTuple
   */
  inline VarTuple(list<VarExp*>* inVars): vars(*inVars){}
  inline VarTuple() {}
  /*!
   * VarTuple Copy Constructor
   *
   * Deep copy constructor, will create a working copy of everything VarTuple
   * points at.
   */
  VarTuple(const VarTuple& vt);

  /*!
   * VarTuple Destructor
   *
   * Deep destructor, will destruct everything VarTuple is pointing at.
   */
  ~VarTuple();

  /*!
   * Calls the copy constructor of VarTuple and returns a reference to the new
   * copy
   * @param vt the VarTuple to be copied
   * @return a reference to the copy of the given VarTuple
   */
  VarTuple& operator=(const VarTuple& vt);

  /*!
   * @return the number of variables in the tuple
   */
  inline const int varSize() const {return vars.size();}

  /*!
   * @return the VarTuple node in string form
   */
  const string toString();

  /*!
   * @return the beginning of the list of variables
   */
  inline const list<VarExp*>::iterator begin(){return vars.begin();}

  /*!
   * @return the end of the list of variables
   */
  inline const list<VarExp*>::iterator end(){return vars.end();}

  void apply(VisitorAdapter* visitor);

  ///@return the list of VarExp pointers
  inline const list<VarExp*> getVars(){return vars;}

  inline const string getNodeType(){return "VarTuple";}

private:

  ///list of variables in the tuple
  list<VarExp*> vars;
};

/*!
 * \class Constraint
 *
 * \brief Base Constraint class for Equality and Inequality nodes in the AST
 *
 * This class is the base class for representing different types of constraints
 * (equality, inequality, greater than, less than, etc.). This class is used for
 * simple methods that both Equality and Inequality have in common, for example
 * the applyExp method. It also enforces that both Equality and Inequality will
 * have a toString method
 */
class Constraint: public Node{
public:

  inline Constraint(){}
  /*!
   * Constraint constructor
   * @param inEq boolean checking whether or not the constraint is an equality
   *             or not
   * @param inExp expression to take in
   */
  inline Constraint(bool inEq, NormExp* inExp): equality(inEq), exp(inExp) {}

  /*!
   * Constraint Destructor
   *
   * Destructs the expression that is being pointed at
   */
  virtual inline ~Constraint(){ delete exp; }

  /*!
   * Returns the string form of constraint
   * @return the string form of constraint
   */
  virtual const string toString() = 0;

  ///@return true if an Equality, false if an Inequality
  const bool getEquality(){return equality;}

  ///@return a pointer to the Expression used
  const NormExp* getExpression(){return exp;}

  ///Calls visitor.ConjunctCase(this);
  virtual void apply(VisitorAdapter* visitor);

  ///Helper method to use the apply method on the expression
  inline void applyExp(VisitorAdapter* visitor){exp->apply(visitor);}

protected:

  ///variable describing whether or not the constraint is an equality
  bool equality;

  ///the expression that the constraint actually is
  NormExp* exp;
};

/*!
 * \class Equality
 *
 * \brief Equality Constraint class in the AST
 *
 * This class is used for the == constraint
 */
class Equality: public Constraint{
public:

  /*!
   * Equality constructor, automatically sets equality to true
   * @param inExp expression that is defining the constrain
   */
  inline Equality(NormExp* inExp): Constraint(true, inExp){}

  /*!
   * Equality Copy Constructor
   *
   * Deep copy constructor, will create a working copy of the NormExp
   */
  Equality(const Equality& eq): Constraint(true, new NormExp(*eq.exp)){}

  /*!
   * @param eq a reference of an Equality to copy.
   * @return a reference to a copy of an Equality
   */
  inline Equality& operator=(const Equality& eq){Equality* neq = new Equality(eq); return *neq;}

  ///@return Equality in string form
  inline const string toString() { return "Equality[" + exp->toString() + "]"; }

  ///Calls visitor.EqualityCase(this);
  void apply(VisitorAdapter* visitor);

  inline const string getNodeType(){return "Equality";}
};


/*!
 * \class Inequality
 *
 * \brief Inequality Constraint class in the AST
 *
 * This class is used for everything that is not the == constraint
 */
class Inequality: public Constraint{
public:

  /*!
   * Inequality constructor, automatically sets equality to false
   * @param inExp expression that is defining the constrain
   */
  inline Inequality(NormExp* inExp): Constraint(false, inExp){}

  /*!
   * Inequality Copy Constructor
   *
   * Deep copy constructor, will create a working copy of the NormExp
   */
  Inequality(const Inequality& eq): Constraint(false, new NormExp(*eq.exp)){}

  /*!
   * @param eq a reference of an Inequality to copy.
   * @return a reference to a copy of an Inequality
   */
  inline Inequality& operator=(const Inequality& eq){Inequality* neq = new Inequality(eq); return *neq;}

  //@return Inequality in string form
  inline const string toString() { return "Inequality[" + exp->toString() + "]"; }

  ///Calls visitor.InequalityCase(this);
  void apply(VisitorAdapter* visitor);

  inline const string getNodeType(){return "Inequality";}
};

/*!
 * \class Conjunction
 *
 * \brief A list of constraints joined by the logical AND operator for the AST.
 *
 * This class sets up a list of constraints that are conjuncted together
 * (ie. x == y && y == z). They must be connected by the logical AND operator.
 */
class Conjunction: public Node{
public:

  /*!
   * Conjunction constructor
   * @param inCons list of constraints that are conjuncted together
   */
  Conjunction(VarTuple* varin, VarTuple* varout, list<Constraint*>* inCons): inVars(varin), outVars(varout), constraints(*inCons){}
  inline Conjunction() {}
  /*!
   * Conjunction Copy Constructor
   *
   * Deep copy constructor, will create a working copy of everything Conjunction
   * points at.
   */
  Conjunction(const Conjunction& con);

  /*!
   * Conjunction Destructor
   *
   * Deep destructor, will destruct everything Conjunction is pointing at.
   */
  ~Conjunction();
  /*!
   * Calls the copy constructor of Conjunction and returns a reference to the new
   * copy
   * @param con The conjunction to be copied
   * @return the reference to the new copy of conjunction
   */
  Conjunction& operator=(const Conjunction& con);

  /*!
   * @return the number of constraints
   */
  inline const int constraintSize() const {return constraints.size();}

  /*!
   * @return Conjunction in string form
   */
  const string toString();

  ///Calls visitor.ConjunctionCase(this)
  void apply(VisitorAdapter* visitor);

  /*!
   * @return the beginning of the list of constraints
   */
  inline const list<Constraint*>::iterator begin() {return constraints.begin();}

  /*!
   * @return the end of the list of constraints
   */
  inline const list<Constraint*>::iterator end() {return constraints.end();}

  inline const string getNodeType(){return "Conjunction";}

  /*!
   * returns the size of the variable tuple
   * @return the size of the variable tuple
   */
  const int getTupleSize() const{return inVars->varSize();}

  /*!
   * helper method for using the apply method to the VarTuple
   */
  inline void applyVars(VisitorAdapter* visitor){inVars->apply(visitor);}

  ///@return a pointer to the VarTuple
  inline const VarTuple* getVars(){return inVars;}

  ///@return the size of the input VarTuple
  inline const int getInSize() const { return inVars->varSize();}

  ///@return the size of the output VarTuple
  inline const int getOutSize() const {return outVars->varSize();}

  ///@return the total size of the VarTuples
  inline const int getTotalSize() const {return getInSize() + getOutSize();}

  ///Helper method for using the apply method on the input VarTuple
  inline void applyInVars(VisitorAdapter* visitor){inVars->apply(visitor);}

  ///Helper method for using the apply method on the output VarTuple
  inline void applyOutVars(VisitorAdapter* visitor){outVars->apply(visitor);}

  ///@return a pointer to the input VarTuple
  inline const VarTuple* getInVars(){return inVars;}

  ///@return a pointer to the output VarTuple
  inline const VarTuple* getOutVars(){return outVars;}

private:

  ///A list of pointers to Equality Constraints or Inequality Constraints
  list<Constraint*> constraints;

  ///The input VarTuple
  VarTuple* inVars;

  ///The output VarTuple
  VarTuple* outVars;
};

/*!
 * \class PresForm
 *
 * \brief base Presburger form Class/Node in AST
 *
 * This class is the base Presburger form class which is used by PresSet and
 * PresRelation
 */
class PresForm: public Node{
public:

  /*!
   * returns the PresForm node in string form
   * @return the PresForm node in string form
   */
  virtual const string toString() = 0;

  void apply(VisitorAdapter* visitor);
};

/*!
 * \class PresSet
 *
 * \brief Presberger Set Class/Node in AST
 *
 * This class takes a set of variables and conjunction along with some symbolics
 * to actually form the root node
 */
class PresSet : public PresForm{
public:

  /*!
   * PresSet Constructor
   * @param varset the Variable set that is involved with the PresSet
   * @param inConjunct the conjunction that is applied to the variable set
   * @param inSymbolics the symbolics (i.e. constants) that are being used in
   *                    the conjuction
   */
  PresSet(list<Conjunction*> inConjunct, list<string>* inSymbolics): conjuncts(inConjunct), symbolics(*inSymbolics){};

  /*!
   * PresSet Constructor that takes no symbolic list
   * @param varset The variable set that is involved with the PresSet
   * @param inConjunct the conjunction that is applied to the variable set
   */

  inline PresSet(list<Conjunction*> inConjunct): conjuncts(inConjunct) {};

  /*!
   * PresSet Copy Constuctor
   *
   * Deep copy constructor, will create a working copy of everything PresSet
   * points to
   * @param ps the PresSet to copy
   */
  PresSet(const PresSet& ps);

  /*!
   * PresSet Destructor
   *
   * Deep destructor, will destroy everything PresSet points to
   */
  ~PresSet();

  /*!
   * Calls the copy constructor of PresSet and returns a reference to the new
   * copy
   * @param ps the PresSet to be copied
   * @return the reference to the new copy of PresSet
   */
  PresSet& operator=(const PresSet& ps);

  /*!
   * returns the PresSet node in string form
   * @return the PresSet node in string form
   */
  const string toString();

  void apply(VisitorAdapter* visitor);

  /*!
   * helper method for using the apply method to the Conjunct
   */
  void applyConjunct(VisitorAdapter* visitor);

  ///@return a pointer to the Conjunction
  inline const list<Conjunction*> getConjuncts(){return conjuncts;}

  ///@return a pointer to the list of symbolics
  inline const list<string>* getSymbolics(){return &symbolics;}

  inline const string getNodeType(){return "PresSet";}

private:

  ///The Conjunction that is applied to the variable set
  list<Conjunction*> conjuncts;

  ///The symbolics that are being used in the conjuction
  list<string> symbolics;
};
/*!
 * \class PresRelation
 *
 * \brief Presberger Relation Class/Node in AST
 *
 * This class takes two sets of variables and conjunction along with some
 * symbolics to actually form the root node. The first set of variables are the
 * ones that are effect by the conjunction and then placed in the second set of
 * variables
 */
class PresRelation : public PresForm{
public:

  /*!
   * PresRelation Constructor
   * @param inVarSet input variable set
   * @param outVarSet output variable set
   * @param inConjunct the conjunction that is applied to the variable set
   * @param inSymbolics the symbolics (i.e. constants) that are being used in
   *                    the conjuction
   */
  PresRelation(list<Conjunction*> inConjunct, list<string>* inSymbolics) : conjuncts(inConjunct), symbolics(*inSymbolics){}

  /*!
   * PresRelation Constructor that has no list of symbolics
   * @param inVarSet input variable set
   * @param outVarSet output variable set
   * @param inConjunct the conjunction that is applied to the variable set
   */
  PresRelation(list<Conjunction*> inConjunct) : conjuncts(inConjunct) {}

  /*!
   * PresRelation Copy Constructor
   *
   * Deep copy constructor, creates a working copy of everything the class
   * points to.
   * @param pr the PresRelation to be copied.
   */
  PresRelation(const PresRelation& pr);

  /*!
   * PresRelation Destructor
   *
   * Deep destructor, will destruct everything the class points to.
   */
  ~PresRelation();

  /*!
   * Calls the copy constructor of PresRelation and returns a reference to the
   * new copy.
   * @param pr the PresRelation to be copied
   * @return a reference to the new PresRelation
   */
  PresRelation& operator=(const PresRelation& pr);

  ///@return PresRelation in string form.
  const string toString();

  void apply(VisitorAdapter* visitor);

  ///Helper method for using the apply method on the Conjunction
  void applyConjunct(VisitorAdapter* visitor);

  ///@return a pointer to the Conjunction
  inline const list<Conjunction*> getConjuncts(){return conjuncts;}

  ///@return a pointer to the list of symbolics
  inline const list<string>* getSymbolics(){return &symbolics;}

  inline const string getNodeType(){return "PresRelation";}

private:

  ///The conjunction that is applied to both VarTuples
  list<Conjunction*> conjuncts;

  ///The list of symbolics
  list<string> symbolics;
};

}//end namespace iegenlib

#endif /* AST_H_ */
